package com.lbj.mall.auth.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.lbj.mall.auth.entity.vo.UserLoginVo;
import com.lbj.mall.auth.entity.vo.UserRegisterVo;
import com.lbj.mall.auth.feign.MemberFeign;
import com.lbj.mall.auth.feign.ThirdPartyFeign;
import com.lbj.mall.common.constant.APPConstant;
import com.lbj.mall.common.entity.vo.MemberVo;
import com.lbj.mall.common.exception.AppException;
import com.lbj.mall.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@RequestMapping("/auth")
@Controller
public class LoginController {

    @Autowired
    ThirdPartyFeign thirdPartyFeign;
    @Autowired
    StringRedisTemplate redis;
    @Autowired
    MemberFeign memberFeign;

    /**
     * 登录：因为是从from表单传过来的数据（key-value形式），所以不需要添加@RequestBody注解
     */
    @PostMapping("/login")
    public String login(UserLoginVo loginVo, RedirectAttributes redirectAttributes, HttpSession session) {
        R loginR = memberFeign.login(loginVo);

        if (loginR.getCode() != 0) {
            Map<String, Object> errorMap = new HashMap<>(1);
            errorMap.put("msg", loginR.get("msg"));
            redirectAttributes.addFlashAttribute("error", errorMap);
            return "redirect:http://auth.localmall.com/login.html";
        }
        MemberVo memberVo = JSON.parseObject(JSON.toJSONString(loginR.get("data")), new TypeReference<>() {
        });
        log.info("giteeLogin_memberVo ===> " + memberVo);
        session.setAttribute(APPConstant.SAVE_LOGIN_USER, memberVo);
        return "redirect:http://www.localmall.com";
    }

    /**
     * TODO 这里还需要接口防刷
     */
    @GetMapping("/send/email")
    @ResponseBody
    public R sendCode(@RequestParam("email") String email) {
        if (StringUtils.isEmpty(email)) {
            return R.error(AppException.EMAIL_ILLEGAL.getCode(), AppException.EMAIL_ILLEGAL.getMsg());
        }
        String key = APPConstant.EMAIL + email;

        String value = redis.opsForValue().get(key);
        // redis中没有验证码或验证码过期就可以重新发送验证码
        if (!StringUtils.isEmpty(value)) {
            // redis中有验证码，需要判断距离上次发送验证码时间是否已经超过一分钟
            long time = Long.parseLong(value.split("_")[1]);
            if (System.currentTimeMillis() - time < 60 * 1000) {
                return R.error(AppException.EMAIL_SEND.getCode(), AppException.EMAIL_SEND.getMsg());
            }
        }

        StringBuilder code = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            code.append(new Random().nextInt(10));
        }
        // 调用远程服务发送验证码
        thirdPartyFeign.sendSms(email, code.toString());
        // 将验证码缓存到redis中，有效时间为10分钟
        code.append("_" + System.currentTimeMillis());
        log.info("生成的邮箱验证码为：" + code);
        redis.opsForValue().set(key, code.toString(), 10, TimeUnit.MINUTES);

        return R.ok();
    }

    /**
     * 注册
     * <p>
     * 注解 Valid 开启验证
     * BindingResult 开启验证后的结果
     * RedirectAttributes使用重定向参数,这里有很多问题
     * - 1.返回值：redirect:http://auth.localmall.com/register.html参数问题
     * - 2.使用转发和重定向
     * - 3.为什么使用RedirectAttributes而不是使用Model
     * <p>
     * （1）使用转发 forward:/register.html或者forward:完整连接
     * 会报错：Request method 'POST' not supported，这是因为当前 register 接口是一个POST请求
     * 而转发则会转发的新的页面也是使用POST方式，但是我们在路径映射的情况下AppMVCConfig类中，使用
     * 的方式是GET方式，所以这里是不能使用转发的。
     * （2）如果返回值使用 "redirect:/register.html"，则会跳转到
     * “http://192.168.1.104:20000/register.html” 这个地址下，是用ip跳转的，而不是使用域名，
     * 因此这里我们需要使用完整的域名
     * （3）这里为什么不能使用 Model 进行传参，因为重定向使用Model会丢失数据，（可以理解一下，如果使用
     * Model传参，在重定向的情况下我们就可以把数据传给到Baidu等网站了）
     * （4）因此这里我们使用 RedirectAttributes 进行传参，RedirectAttributes 的实现原理是使用
     * Session 的技术实现，因此可以达到重定向时进行传参
     * （5）因为RedirectAttributes使用Session技术，而【【Session在分布式的情况下有问题】】，我们需要解决
     */
    @PostMapping("/register")
    public String register(@Valid UserRegisterVo registerVo, BindingResult result,
                           RedirectAttributes redirectAttributes,
                           Model model) {
        // 注册字段验证有错误
        if (result.hasErrors()) {
            Map<String, Object> error = result.getFieldErrors().stream().collect(Collectors.toMap(
                    FieldError::getField, FieldError::getDefaultMessage
            ));
            // 重定向的情况下使用model传参会没有数据的
            //model.addAttribute("error", error);

            // 将错误信息返回给前端页面
            // addFlashAttribute 只获取一次后就删除了
            redirectAttributes.addFlashAttribute("error", error);
            // 注册失败，重定向到注册页面
            return "redirect:http://auth.localmall.com/register.html";
        }

        // 这里开始真正的注册逻辑

        // 1.获取redis中保存的验证码的key
        String key = APPConstant.EMAIL + registerVo.getEmail();
        // 2.获取redis中保存的验证码的值
        String code = redis.opsForValue().get(key);
        // 3.判断获取的验证码和页面传入的验证码是否相等
        if (code == null || !registerVo.getCode().equals(code.split("_")[0])) {
            // 4.验证码验证不通过，返回错误信息
            Map<String, Object> error = new HashMap<>(1);
            error.put("code", "验证码输入有误");
            redirectAttributes.addFlashAttribute("error", error);
            return "redirect:http://auth.localmall.com/register.html";
        }
        // 5.验证码通过验证，删除掉redis中保存的数据
        redis.delete(key);
        // 6.验证码通过验证，可以调用远程服务注册用户信息
        R registerR = memberFeign.register(registerVo);
        // 7.调用远程服务报错
        if (registerR.getCode() != 0) {
            Map<String, Object> error = new HashMap<>(1);
            error.put("msg", registerR.get("msg"));
            redirectAttributes.addFlashAttribute("error", error);
            return "redirect:http://auth.localmall.com/register.html";
        }
        // 8.调用远程服务成功

        // 注册成功，重定向到登录页面
        return "redirect:http://auth.localmall.com/login.html";
    }
}
